/**
 * Copyright (c) 2007-2008 Nearchos Paspallis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * AspectSense
 *
 * Middleware for annotation-based, context-aware and adaptive services for
 * mobile and ubiquitous computing
 *
 * This project is an open-source effort, initiated by Nearchos Paspallis as
 * part of his PhD work at the University of Cyprus (http://www.ucy.ac.cy).
 *
 * More information about the project is available at "http://aspectsense.org".
 */

package org.aspectsense.framework;

import org.aspectsense.graphs.Node;
import org.aspectsense.graphs.DirectedEdge;
import org.aspectsense.graphs.DefaultDirectedBipartiteGraph;
import org.aspectsense.graphs.DefaultDirectedEdge;

import java.util.*;

/**
 * @author Nearchos Paspallis [nearchos@cs.ucy.ac.cy]
 *         Date: Dec 31, 2007
 *         Time: 10:59:38 AM
 */
class DeactivationPlan
{
    private final Vector<Node<String>> orderedNodes = new Vector<Node<String>>();

    private final Vector<DirectedEdge<String>> orderedDirectedEdges = new Vector<DirectedEdge<String>>();

    public static final DeactivationPlan EMPTY_DEACTIVATION_PLAN = new DeactivationPlan();

    /**
     * Constructs an empty deactivation plan.
     */
    private DeactivationPlan()
    {
        // empty
    }

    /**
     * Constructs a new {@link org.aspectsense.framework.DeactivationPlan}. The
     * computed plan dictates in which order should the involved components be
     * deactivated and in which order should the involved links be destroyed.
     *
     * @param directedBipartiteGraph the {@link
     * org.aspectsense.graphs.DirectedBipartiteGraph} which specifies the
     * composition plan on which the deactivation will be applied
     * @throws NullPointerException if either of the parameters is null
     */
    DeactivationPlan(final DefaultDirectedBipartiteGraph<String> directedBipartiteGraph)
    {
        super();

        if(directedBipartiteGraph == null)
        {
            throw new NullPointerException("Illegal null argument");
        }

        // an empty input results to an empty deactivation plan
        if(directedBipartiteGraph.isEmpty())
        {
            return;
        }

        final Set<Node<String>> leftPartition = directedBipartiteGraph.getLeftPartition();

        // compute the root nodes
        final Set<Node<String>> rootNodes = new HashSet<Node<String>>();
        for(final Node<String> leftNode : leftPartition)
        {
            if(directedBipartiteGraph.getDirectedEdgesTo(leftNode).isEmpty())
            {
                rootNodes.add(leftNode);
            }
        }

        // construct the deactivation plan!
        final Queue<Node<String>> processingQueue = new LinkedList<Node<String>>();
        processingQueue.addAll(rootNodes);

        final Vector<DirectedEdge<String>> tempOrderedDirectedEdges = new Vector<DirectedEdge<String>>();

        while(!processingQueue.isEmpty())
        {
            final Node<String> currentNode = processingQueue.poll();

            if(!leftPartition.contains(currentNode))
            {
                // assert rightPartiton.contains(currentNode);
                orderedNodes.add(currentNode);
            }

            final Set<DirectedEdge<String>> directedEdgesFromCurrentNode
                    = directedBipartiteGraph.getDirectedEdgesFrom(currentNode);
            for(final DirectedEdge<String> directedEdge : directedEdgesFromCurrentNode)
            {
                tempOrderedDirectedEdges.add(directedEdge);
                processingQueue.add(directedEdge.getToNode());
            }
        }

        // post-process the edges to point from components to components only!
        final Map<Node<String>, Node<String>> serviceProviders = new HashMap<Node<String>, Node<String>>();

        for(final DirectedEdge<String> directedEdge : tempOrderedDirectedEdges)
        {
            final Node<String> fromNode = directedEdge.getFromNode();

            if(leftPartition.contains(fromNode))
            {
                serviceProviders.put(fromNode, directedEdge.getToNode());
            }
            // else assert rightPartition.contains(fromNode);
        }

        for(final DirectedEdge<String> directedEdge : tempOrderedDirectedEdges)
        {
            final Node<String> fromNode = directedEdge.getFromNode();
            final Node<String> toNode = directedEdge.getToNode();

            if(!leftPartition.contains(fromNode))
            {
                // assert rightPartition.contains(fromNode);

                final Node<String> serviceProvider = serviceProviders.get(toNode);
                orderedDirectedEdges.add(new DefaultDirectedEdge<String>(fromNode, serviceProvider));
            }
        }
    }

    public Vector<Node<String>> getOrderedNodes()
    {
        return new Vector<Node<String>>(orderedNodes);
    }

    public Vector<DirectedEdge<String>> getOrderedDirectedEdges()
    {
        return new Vector<DirectedEdge<String>>(orderedDirectedEdges);
    }

    private static String lastPart(final Object object)
    {
        final String string = object.toString();

        int index = string.lastIndexOf('.');
        if(index == -1)
        {
            return string;
        }
        else
        {
            return string.substring(index + 1);
        }
    }

    public String toString()
    {
        final StringBuilder stringBuilder = new StringBuilder();

        stringBuilder.append("Deactivation plan: \n  Deactivate nodes in order: ");

        for(final Node<String> node : orderedNodes)
        {
            stringBuilder.append(lastPart(node)).append(" ");
        }

        stringBuilder.append("\n  Disconnect links in order: ");

        for(final DirectedEdge<String> directedEdge : orderedDirectedEdges)
        {
            stringBuilder.append(lastPart(directedEdge.getFromNode())).append("->").append(lastPart(directedEdge.getToNode())).append(" ");
        }

        return stringBuilder.toString();
    }
}